/*
 * DrawerVisitor.cpp
 *
 *  Created on: 06-01-2011
 *      Author: Przemek
 */

#include "world/visitors/DrawerVisitor.hpp"
#include "utils/GLUtils.hpp"
#include "utils/MathUtils.hpp"
#include "world/visitors/BarriersGetterVisitor.hpp"
#include "pch.hpp"
using namespace boost;
using namespace std;

void DrawerVisitor::visit(Barrier& barrier)
{
	barriers.push_back(&barrier);
}

void DrawerVisitor::visit(Individual& individual)
{
	individuals.push_back(&individual);
	radars.push_back(individual.getRadar());
}

void DrawerVisitor::visit(MeatSupply& meatSupply)
{
	meatSupplies.push_back(&meatSupply);
}

void DrawerVisitor::visit(VegetableSupply& vegetableSupply)
{
	vegetableSupplies.push_back(&vegetableSupply);
}

void DrawerVisitor::visit(WaterSupply& waterSupply)
{
	waterSupplies.push_back(&waterSupply);
}

void DrawerVisitor::setSelectedElementCenter(const Vector& selectedElementCenter)
{
	this->selectedElementCenter = boost::shared_ptr<Vector>(new Vector(selectedElementCenter));
}

void DrawerVisitor::flush()
{
	glBegin( GL_QUADS);
	drawMeatSupplies();
	drawVegetableSupplies();
	drawWaterSupplies();
	drawBarriers();
	glEnd();

	drawIndividuals();

	glEnable( GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	drawRadars();
	glDisable(GL_BLEND);

	if(selectedElementCenter.get() != 0)
	{
		markSelected();
	}
}

void DrawerVisitor::drawMeatSupplies()
{
	BOOST_FOREACH(MeatSupply* meatSupply, meatSupplies)
				{
					drawBlackFramedRectangle(meatSupply, GLColor::RED);
				}
}

void DrawerVisitor::drawBlackFramedRectangle(Element* element, const GLColor& color)
{
	GLColor::BLACK.setGLState();
	GLUtils::pushVerticesInQuad(element->getPosition(), element->getSize());

	color.setGLState();
	GLUtils::pushVerticesInQuad(element->getPosition() + Vector(1, 1), element->getSize()
			- Dimension(2, 2));
}

void DrawerVisitor::drawVegetableSupplies()
{
	BOOST_FOREACH(VegetableSupply* vegetableSupply, vegetableSupplies)
				{
					drawBlackFramedRectangle(vegetableSupply, GLColor::GREEN);
				}
}

void DrawerVisitor::drawBarriers()
{
	BOOST_FOREACH(Barrier* barrier, barriers)
				{
					drawBlackFramedRectangle(barrier, GLColor(0.52f, 0.25f, 0.0f));
				}
}

void DrawerVisitor::drawWaterSupplies()
{
	static const GLColor waterColor(0.0f, 0.5f, 1.0f);
	BOOST_FOREACH(WaterSupply* waterSupply, waterSupplies)
				{
					drawBlackFramedRectangle(waterSupply, waterColor);
				}
}

void DrawerVisitor::drawIndividuals()
{
	BOOST_FOREACH(Individual* individual, individuals)
				{
					drawIndividual(individual);
				}
}

void DrawerVisitor::drawIndividual(Individual* individual)
{
	static const int SIZE = Individual::SIZE;
	const Vector& direction = individual->getLookDirection();
	const float angleInDegrees = -MathUtils::toDegreeses((atan2(direction.getX(), direction.getY())));
	const Vector& center = individual->getBounds().getCenter();

	glTranslatef(center.getX(), center.getY(), 0.0f);
	glRotatef(angleInDegrees, 0.0f, 0.0f, 1.0f);
	glTranslatef(-SIZE / 2, -SIZE / 2, 0.0f);

	GLColor::BLACK.setGLState();
	GLUtils::drawRectangle(Vector(0, 0), Dimension(SIZE, SIZE));

	if(individual->getGenotype().isCarnivore())
	{
		GLColor::RED.setGLState();
	}
	else
	{
		GLColor::GREEN.setGLState();
	}
	glBegin( GL_TRIANGLES);
	glVertex2i(0, 0);
	glVertex2i(SIZE, 0);
	glVertex2i(SIZE / 2, SIZE);
	glEnd();

	glTranslatef(SIZE / 2, SIZE / 2, 0.0f);
	glRotatef(-angleInDegrees, 0.0f, 0.0f, 1.0f);
	glTranslatef(-center.getX(), -center.getY(), 0.0f);
}

void DrawerVisitor::drawRadars()
{
	glBegin( GL_TRIANGLES);
	BOOST_FOREACH(CircleSlice& radar, radars)
				{
					drawRadar(radar);
				}
	glEnd();
}

void DrawerVisitor::drawRadar(CircleSlice& radar)
{
	const Vector& center = radar.getPosition();
	const float step = MathUtils::toRadians(10);
	const float minAngle = radar.getMinAngleInRadians();
	const float maxAngle = radar.getMaxAngleInRadians() - step;

	static const float blendMaxValue = 0.3f;
	static const float blendMinValue = 0.0f;

	for(float i = minAngle; i <= maxAngle; i += step)
	{
		const Vector& firstPoint = MathUtils::getUnitCircleCoordinatesForAngle(
				MathUtils::toDegreeses(i)) * radar.getRadius() + center;
		const Vector& secondPoint = MathUtils::getUnitCircleCoordinatesForAngle(MathUtils::toDegreeses(
				i + step)) * radar.getRadius() + center;

		GLColor::WHITE.setGLState(blendMaxValue);
		glVertex2f(center.getX(), center.getY());
		GLColor::WHITE.setGLState(blendMinValue);
		glVertex2f(firstPoint.getX(), firstPoint.getY());
		glVertex2f(secondPoint.getX(), secondPoint.getY());
	}
}

void DrawerVisitor::markSelected()
{
	static const float HEIGHT = 40.0f;
	static const float SIDE = 2.0f / sqrtf(3.0f) * sqrtf(HEIGHT * HEIGHT);
	static const GLColor SELECTION_COLOR(0.3f, 0.6f, 0.8f);
	static float angle = 0.0f; // for simple and ugly selection animation
	SELECTION_COLOR.setGLState();

	angle += 0.1f;
	glTranslatef(selectedElementCenter->getX(), selectedElementCenter->getY(), 0.0f);
	glRotatef(angle, 0.0f, 0.0f, 1.0f);
	glBegin( GL_LINE_LOOP);
	glVertex2f(0, -HEIGHT * 2 / 3);
	glVertex2f(SIDE / 2, HEIGHT / 3);
	glVertex2f(-SIDE / 2, HEIGHT / 3);
	glEnd();
	glRotatef(-angle, 0.0f, 0.0f, 1.0f);
	glTranslatef(-selectedElementCenter->getX(), -selectedElementCenter->getY(), 0.0f);
}
